<?php
require('../bj_init.php');
$offset = 60*60*24*60;
$ExpStr = "Expires: ".gmdate("D, d M Y H:i:s",time() + $offset)." GMT";
$LmStr = "Last-Modified: ".gmdate("D, d M Y H:i:s",filemtime(__FILE__))." GMT";
header("Cache-Control: public");
header("Pragma: cache");
header($ExpStr);
header($LmStr);
header('Content-Type: text/javascript; charset: UTF-8');
?>
/*
Script: postEditor.js
	Using postEditor you can tabulate without losing your focus and maintain the tabsize in line brakes. 
	You can also use snippets like in TextMate.

Dependencies:
	<http://mootools.net>

Author:
	Daniel Mota aka IceBeat, <http://icebeat.bitacoras.com>

Contributors:
  Sergio Ãlvarez aka Xergio, <http://xergio.net>
  Jordi Rivero aka Godsea, <http://godsea.dsland.org>

License:
	MIT-style license.
*/
var postEditor = {};
postEditor.create = new Class({
  
  tab:"	",
  
  setOptions: function(options){
		this.options = Object.extend({
      snippets :          {},
      smartTypingPairs :  {},
      selections :        {}
		}, options || {});
	},
	
	initialize: function(el,next, options){
	  if(window.ActiveXObject) return;
		this.element = $(el);
		this.next = $(next);
		this.setOptions(options);
		this.styles = {
		  line_height: this.element.getStyle('line-height').toInt() || 14,
		  font_size: this.element.getStyle('font-size').toInt() || 11,
		  height: this.element.getStyle('height').toInt()
		};
		this.autoTab = null;
    this.ssKey = 0;
    this.seKey = 0;
    this.completion = null;
    this.tabl = this.tab.length;
    this.element.onkeypress = this.onKeyPress.bind(this);
	},

  changeSnippets: function(snippets) {
    this.options.snippets = snippets || {};
  },
  
  changeSmartTypingPairs: function(smartTypingPairs) {
    this.options.smartTypingPairs = smartTypingPairs || {};
  },
  
  changeSelections: function(selections) {
    this.options.selections = selections || {};
  },
  
  ss: function() {
    return this.element.selectionStart;
  },
  
  se: function() {
    return this.element.selectionEnd;
  },
  
  slice: function(start,end) {
    return this.element.value.slice(start,end);
  },
  
  value: function(value) {
    this.element.value = value.join("");
  },
  
  getStart: function(rest) {
		var rest = rest ? rest.length : 0;
		return this.slice(0,this.ss()-rest);
	},
	
	getEnd: function(rest) {
	  var rest = rest ? rest.length : 0;
		return this.element.value.slice(this.se()-rest);
	},
	
	selectRange: function(start,end) {
		this.element.selectionStart = start;
		this.element.selectionEnd   = start+end;
	},
	
	focus: function(focus,type) {
	  if(type) {
	    this.scrollTop  = this.element.scrollTop;
      this.scrollLeft = this.element.scrollLeft;
    } else {
      this.element.scrollTop  = this.scrollTop;
	    this.element.scrollLeft = this.scrollLeft;
	  }
    if(focus) this.element.focus();
	},
	
	updateScroll: function() {
	  var lines = this.getStart().split("\n").length,
		    height = (lines-Math.round(this.element.scrollTop/this.styles.line_height))*this.styles.line_height;
		height += this.styles.line_height;
		if(height>=this.styles.height) this.element.scrollTop += this.styles.line_height;
    this.focus(true,1);
	},
	
	onKeyPress: function(e) {
	  if(this.filterByNext(e)) return;
	  if(this.filterByPairs(e)) return;
	  
    this.filterBySelect(e);
	  if(this.filterByTab(e)) return;
    if([13,9,8,46].test(e.keyCode)) this.focus(false,true);
    switch(e.keyCode) {
      case 27:  this.completion=null;
                this.autoTab=null;  break;
			case 39:  this.onKeyRight(e); break;
      case 13:  this.onEnter(e);    break;
	    case  9:  this.onTab(e);      break;
		  case  8:  this.onBackspace(e);break;
		  case 46:  this.onDelete(e);   break;
		}
		if([13,9,8,46].test(e.keyCode)) this.focus(true,false);
  },
  
  filterByNext: function(e) {
    if(e.shiftKey && e.keyCode==13) {
	    if(this.next) {
	      e.preventDefault();
	      this.next.focus();
	      return true;
	    }
	  }
	  return false;
  },
  
  filterByPairs: function(e) {
    var charCode = String.fromCharCode(e.charCode),
        stpair = this.options.smartTypingPairs[charCode];
    if(stpair) {
	    if($type(stpair) == 'string') stpair= { pair : stpair };
      if(!stpair.scope || this.scope(stpair.scope)) {
        var ss = this.ss(), se = this.se(), start = this.getStart();
        if(ss == se) {
          this.value([start,stpair.pair,this.getEnd()]);
          this.selectRange(start.length,0);
        } else {
          e.preventDefault();
          this.ssKey = ss;
    	    this.seKey = se;
          this.value([start,charCode,this.slice(ss,se),stpair.pair,this.getEnd()]);
          this.selectRange(ss+1,se-ss);
        }
      }
      stpair = null;
	    return true;
	  }
	  return false;
  },
  
  filterBySelect: function(e) {
    var charCode = String.fromCharCode(e.charCode);
    if(e.ctrlKey && e.shiftKey) {
	    if([0,1,2,3,4,5,6,7,8,9].test(charCode)) {
        var fn = this.options.selections[charCode];
        if(fn) {
          var ss = this.ss(), se = this.se(), 
              sel = fn.apply(this, [this.slice(ss,se)]);
  	      if(sel) {
  	        var start = this.getStart();
  	        if($type(sel) == 'array') {
  	          this.value([start,sel.join(""),this.getEnd()]);
              this.selectRange(start.length+sel[0].length,sel[1].length);
            } else {
              if(sel.selection) {
                if(sel.snippet) {
                  start = this.slice(0,sel.selection[0]);
                  var end = this.slice(sel.selection[1],this.element.value.length);
                  this.value([start,sel.snippet.join(""),end]);
                  this.selectRange(start.length+sel.snippet[0].length,sel.snippet[1].length);
                } else {
                  this.selectRange(sel.selection[0],sel.selection[1]);
                }
              } else {
                this.value([start,sel.snippet.join(""),this.getEnd()]);
                this.selectRange(start.length+sel.snippet[0].length,sel.snippet[1].length);
              }
            }
          }
        }
      }
    }
  },
  
  filterByTab: function(e) {
    if(this.autoTab) {
      var ss = this.ss(), se = this.se(), key = this.ssKey, end = this.seKey;
      if(![key+1,key,key-1,end].test(ss)) {
        this.completion = null;
        this.autoTab = null;
      }
      if(this.autoTab && [38,39].test(e.keyCode) && ss==se) {
        this.completion = null;
        this.autoTab = null;
      }
      this.ssKey = ss;
	    this.seKey = se;
    } else {
      this.ssKey = 0;
	    this.seKey = 0;
    }
    return false;
  },
  
  scope: function(scopes) {
    var ss = this.ss(), text = this.getStart();
    for (var key in scopes) {
      if(!key) return true;
      var open = text.lastIndexOf(key);
      if(open > -1) {
        var close = this.slice(open+key.length,ss).lastIndexOf(scopes[key]);
        if(close == -1) return true;
      }
    }
    return false;
  },

	onKeyRight: function(e) {
    var ss = this.ss(), se = this.se(), start = this.getStart();
    if(ss!=se) {
      e.preventDefault();
			this.selectRange(se,0);
    }
  },
  
  onEnter: function(e) {
    this.updateScroll();
    var ss = this.ss(), se = this.se(), start = this.getStart();
    if(ss==se) {
      var line = start.split("\n").pop(),
          tab = line.match(/^\s+/gi);
      if(tab) {
          e.preventDefault(); tab = tab.join("");
          this.value([start,"\n",tab,this.getEnd()]);
          this.selectRange(ss+1+tab.length,0);
      }
    }
  },
  
  onBackspace: function(e) {
    var ss = this.ss(), se = this.se();
    if(ss == se && this.slice(ss - this.tabl,ss) == this.tab) {
			e.preventDefault();
			var start = this.getStart(this.tab), end = this.slice(ss,this.element.value.length);
			if(start.match(/\n$/g) && end.match(/^\n/g)) {
        this.value([start,this.slice(ss-1,this.element.value.length)]);
			} else {
			  this.value([start,end]);
			}
		  this.selectRange(ss - this.tabl,0);
		} else if(ss == se) {
  		  var charCode  = this.slice(ss - 1,ss), 
  		      close     = this.slice(ss,ss+1), 
  		      stpair    = this.options.smartTypingPairs[charCode];
  		  if($type(stpair) == 'string') stpair = { pair : stpair };
  		  if(stpair && stpair.pair == close) {
  		    this.value([this.getStart(stpair.pair),this.slice(ss,this.element.value.length)]);
          this.selectRange(ss,0);
  		  }
  	}
  },
  
  onDelete: function(e) {
    var ss = this.ss(), se = this.se();
    if(ss == se && this.slice(ss,ss+this.tabl) == this.tab) {
			e.preventDefault();
      this.value([this.getStart(),this.slice(ss+this.tabl,this.element.value.length)]);
      this.selectRange(ss,0);
		}
  },
  
  onTab: function(e) {
    e.preventDefault();
    
    var ss = this.ss(), se = this.se(), sel = this.slice(ss,se), text = this.getStart();
    
    if(this.filterCompletion(e,ss,se)) return;
    if(this.filterAutoTab(e,ss,se)) return;
    
    if (ss != se && sel.indexOf("\n") != -1) {
        var newsel = sel.replace(/\n/g,"\n"+this.tab);
        this.value([text,this.tab,newsel,this.getEnd()]);
        this.selectRange(ss + this.tabl,se + (this.tabl*sel.split("\n").length) - ss - this.tabl);
    } else {
        var snippetObj = null;
        for (var key in this.options.snippets) {
          var value = this.options.snippets[key];
          if ($type(value) == 'function') continue;
          if(text.length-key.length==-1) continue;
				  if(text.length-key.length==text.lastIndexOf(key)) {
				    if($type(value) == 'array') value = { snippet:value };
				    snippetObj = Object.extend({},value);
				    break;
				  }
        }
        if(snippetObj && (!snippetObj.scope || this.scope(snippetObj.scope))) {
          
            if(snippetObj.command) {
              var command = snippetObj.command.apply(this, [key]);
              if($type(command) == 'array') snippetObj.snippet = command;
              else snippetObj = command;
            }
            
            var snippet = snippetObj.snippet.copy(), tab = text.split("\n").pop().match(/^\s+/gi),
                start = this.getStart(snippetObj.key || key);
            
            if(tab) {
              tab = tab.join("");
              snippet[0] = snippet[0].replace(/\n/g,"\n"+tab);
              snippet[1] = snippet[1].replace(/\n/g,"\n"+tab);
              snippet[2] = snippet[2].replace(/\n/g,"\n"+tab);
            }
            
            this.value([start,snippet[0],snippet[1],snippet[2],this.getEnd()]);
            
            if(snippetObj.tab) {
              
              this.autoTab = {
                tab: snippetObj.tab.copy(),
                snippet: snippet.copy(),
                start: snippetObj.start
              };
              
              var item = this.autoTab.tab.shift();
              this.autoTab.ss = snippet[1].indexOf(item);
              
              if(this.autoTab.ss > -1) {
                
                this.autoTab.ssLast = start.length+snippet[0].length+this.autoTab.ss;
                this.ssKey = this.autoTab.ssLast;
                this.seKey = this.ssKey+item.length;
                this.completion = null;
                if(snippetObj.completion) {
                  this.autoTab.completion = snippetObj.completion;
                  this.autoTab.item = item;
                  this.autoTab.loop = true;
                  if(typeof snippetObj.loop == 'boolean') this.autoTab.loop = snippetObj.loop;
                  var completion = this.autoTab.completion[item];
                  if(completion) {
                    var i = [item].extend(completion);
                    var a = completion.copy().extend(['']);
                    this.autoTab.index = item;
                    this.completion = a.associate(i);
                  }
                }
                this.selectRange(start.length+snippet[0].length+this.autoTab.ss,item.length);
                
              } else {
                this.autoTab = null;
                this.selectRange(start.length+snippet[0].length,snippet[1].length);
              }
              
            } else {
        	    this.selectRange(start.length+snippet[0].length,snippet[1].length);
      	    }
      	    
        	  snippet = null;
        	  
        } else {
          this.value([text,this.tab,this.slice(ss,this.element.value.length)]);
          if (ss == se) this.selectRange(ss + this.tabl,0);
          else this.selectRange(ss + this.tabl,se - ss);
        }
    }
  },
  
  filterAutoTab: function(e,ss,se) {
    if(this.autoTab) {
      var length = this.autoTab.tab.length;
      if(length) {
        if(this.autoTab.ssLast <= ss) {
          var item = this.autoTab.tab.shift(), 
              next = this.slice(ss,ss+this.autoTab.snippet[1].length-this.autoTab.ss).indexOf(item);
          if(length==1 && !item) {
            var end = this.autoTab.snippet[2].length;
            if($type(this.autoTab.start) == 'number') end = this.autoTab.start;
            else if(this.autoTab.start) end = 0;
            this.selectRange(se+this.getEnd().indexOf(this.autoTab.snippet[2])+end,0);
            this.completion = null;
            return true;
          } else if(next > -1) {
            this.autoTab.ss = next;
            this.autoTab.ssLast = next+ss;
            this.ssKey = this.autoTab.ssLast;
            this.seKey = this.ssKey+item.length;
            this.autoTab.item = item;
            if(this.completion) {
              var completion = this.autoTab.completion[item];
              if(completion) {
                var i = [item].extend(completion);
                var a = completion.copy().extend(['']);
                this.autoTab.index = item;
                this.completion = a.associate(i);
              } else {
                this.completion = null;
              }
            }
            this.selectRange(ss+next,item.length);
            return true;
          } else {
            this.onTab(e);
            return true;
          }
        }
      }
      this.autoTab=null;
    }
    return false;
  },
  
  filterCompletion: function(e,ss,se) {
    if(this.completion && ss==this.ssKey && se==this.seKey && this.autoTab.item.length == se-ss) {
      var item = this.completion[this.autoTab.item];
      if(item) {
        this.seKey = this.ssKey+item.length;
        this.autoTab.item = item;
        this.value([this.getStart(),item,this.getEnd()]);
        this.selectRange(ss,item.length);
        return true;
      } else if(this.autoTab.loop) {
        item = this.autoTab.index;
        this.autoTab.item = item;
        this.seKey = this.ssKey+item.length;
        this.value([this.getStart(),item,this.getEnd()]);
        this.selectRange(ss,item.length);
        return true;
      }
    }
    this.completion = null;
    return false;
  }
});
